#include "kernel/types.h"
#include "user.h"

void sieve(int read_pipe)
{ 
    // 从上一个过滤器读取一个数
    int prime;

    // 递归终点：管道中没有待筛的数
    if (read(read_pipe, &prime, sizeof(int)) == 0) {
        close(read_pipe);
        exit(0);
    }

    // 根据筛法，管道中第一个数为素数，打印素数
    printf("prime %d\n", prime);
    
    // 创建新管道
    int p[2];
    pipe(p);

    // 创建新进程
    int pid = fork();
    
    // fork失败
    if(pid < 0){
        printf("failed to fork!\n");
        exit(-1);
    }
    // 父进程负责筛选后将剩下的数写入管道
    else if (pid > 0){
        // 父进程只写，关闭读端
        close(p[0]);
        
        int num;
        // 保留不能被prime整除的数 写入管道供子进程读取
        while(read(read_pipe, &num, sizeof(int)) > 0) {
            if (num % prime != 0)   write(p[1], &num, sizeof(int));
        }
        
        // 关闭不必要的管道 等待子进程返回
 
        close(p[1]);       close(read_pipe);
        wait(0);     
    }
    // 子进程递归进行筛法
    else{
        close(p[1]);      
        sieve(p[0]);    
    }

    exit(0);

}

int main(int argc, char *argv[]){
    
    //检查参数数量是否正确
    if(argc != 1){
        printf("primes needs not argument!\n"); 
        exit(-1);
    }

    // 创建管道
    int p[2];
    pipe(p);

    // 创建子进程
    int pid = fork();
    
    // fork失败
    if(pid < 0){
        printf("failed to fork!\n");
        exit(-1);
    }
    // 主进程只写
    else if (pid > 0) {  
        // 关闭读端
        close(p[0]);    

        // 将2-35的数写入管道
        for (int i=2; i<=35; ++i)   write(p[1], &i, sizeof(int));
        
        // 关闭写端，等待子进程返回
        close(p[1]);
        wait(0);  
    } 
    // 子进程递归进行筛法
    else {         
        // 子进程只读，故关闭写端   
        close(p[1]);    
        sieve(p[0]);
    }

    exit(0);
}